package com.bestcem.xm.deliver.service.impl;

import cn.hutool.core.collection.ListUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.bestcem.bp.xm.user.grpc.v1.services.CheckUserInfo;
import com.bestcem.xm.common.core.exception.BusinessException;
import com.bestcem.xm.common.core.uitls.DateUtil;
import com.bestcem.xm.component.security.context.SecurityContextHolder;
import com.bestcem.xm.deliver.entity.DeliverDeliver;
import com.bestcem.xm.deliver.entity.DeliverDeliverExtParams;
import com.bestcem.xm.deliver.entity.DeliverExtDeliverMapping;
import com.bestcem.xm.deliver.entity.DeliverSurveyDeliver;
import com.bestcem.xm.deliver.enums.DeliverStatusEnum;
import com.bestcem.xm.deliver.enums.DeliverTtypeEnum;
import com.bestcem.xm.deliver.grpc.client.OpenPlatformGrpcClient;
import com.bestcem.xm.deliver.grpc.client.UserGrpcClient;
import com.bestcem.xm.deliver.grpc.client.dto.OrgInfoDto;
import com.bestcem.xm.deliver.mapper.DeliverDeliverExtParamsMapper;
import com.bestcem.xm.deliver.mapper.DeliverDeliverMapper;
import com.bestcem.xm.deliver.mapper.DeliverExtDeliverMappingMapper;
import com.bestcem.xm.deliver.mapper.DeliverSurveyDeliverMapper;
import com.bestcem.xm.deliver.service.*;
import com.bestcem.xm.qdes.grpc.v1.services.Project;
import com.google.common.collect.HashBiMap;
import com.google.common.collect.Lists;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NonNull;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.BiConsumer;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import static com.bestcem.xm.deliver.enums.DeliverChannelEnum.DELIVER_CHANNEL_CUSTOM_API;
import static com.bestcem.xm.deliver.enums.DeliverChannelEnum.DELIVER_CHANNEL_EMAIL;
import static com.bestcem.xm.deliver.enums.DeliverChannelEnum.DELIVER_CHANNEL_SMS;
import static com.bestcem.xm.deliver.enums.DeliverExtParamsStatusEnum.DELIVEREXTPARAMS_STATUS_DELETE;
import static com.bestcem.xm.deliver.enums.DeliverStatusEnum.*;
import static com.bestcem.xm.deliver.enums.DeliverTtypeEnum.*;
import static com.bestcem.xm.deliver.enums.EmailRecordStatusEnum.EMAILRECORD_STATUS_FAILED;
import static com.bestcem.xm.deliver.enums.EmailRecordStatusEnum.EMAILRECORD_STATUS_SUCCESS;
import static com.bestcem.xm.deliver.enums.WechatMpdeliverMpTypeEnum.WECHATMPDELIVER_MP_TTYPE_CONDITION;

@Slf4j
@Service
@RequiredArgsConstructor
public class DeliverDeliverTriggerServiceImpl implements DeliverDeliverTriggerService, InitializingBean {
    /**
     * BiConsumer<T,U>
     * 代表了一个接受两个输入参数的操作，并且不返回任何结果
     * <p>
     * BiFunction<T,U,R>
     * 代表了一个接受两个输入参数的方法，并且返回一个结果
     * <p>
     * Function<T,R>
     * 接受一个输入参数，返回一个结果
     */
    private final ConcurrentHashMap<DeliverTtypeEnum, BiConsumer<DeliverDeliver, String>> triggerDeliverStatusBiFunctionMap = new ConcurrentHashMap<>();
    private final ConcurrentHashMap<DeliverTtypeEnum, Function<DeliverDeliver, Boolean>> checkDeliverStatusFunctionMap = new ConcurrentHashMap<>();
    private final HashBiMap<String, Integer> event2StatusBiMap = HashBiMap.create();
    private final HashMap<String, EventObj> eventsMap = new HashMap<String, EventObj>() {{
        put("start", new EventObj(Arrays.asList("stop", "review_success", "finish"), "start"));
        put("stop", new EventObj(Arrays.asList("start", "review_success"), "stop"));
        put("finish", new EventObj(Arrays.asList("start", "stop", "pause"), "finish"));
        put("delete", new EventObj(Arrays.asList("start", "stop", "finish", "unreviewed", "review_success", "review_fail", "done", "finish_stop", "pause"), "delete"));
        put("done", new EventObj(Arrays.asList("stop", "review_success"), "done"));
        put("active", new EventObj(Arrays.asList("stop", "pause"), "active"));
    }};
    private final HashMap<Integer, String> deliverCheckUrlDict = new HashMap<Integer, String>() {{
        put(DELIVER_CHANNEL_EMAIL.getValue(), "{}/edit/deliver/{}/main/email/detail/{}?isViewCheck=true");
        put(DELIVER_CHANNEL_SMS.getValue(), "{}/edit/deliver/{}/main/mail/detail/{}?isViewCheck=true");
        put(DELIVER_CHANNEL_CUSTOM_API.getValue(), "{}/edit/deliver/{}/main/api/detail/{}?isViewCheck=true");
    }};
    // 投放审核
    private final String deliverCheckUrlLabel = "<a href=\"{}\" target=\"_blank\">{}</a>";
    private final String deliverCheckMailTemplate ="<div><p></p><p>您好：</p><p>来自{}的{}待审核，请登录到{}-问卷投放模块进行审核</p>"
        + "<p>审核地址：{}</p><p><u>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;</u><u>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;</u><u>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;</u></p>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; "
        + "{}&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; {}<br /><p></p><br /></div>";

    @Value("${app.deliver.survey.system.url:http://cloud-dev.bestcem.cn}")
    private String deliverDomain;
    @Value("${app.xm.name:倍市得CEM}")
    private String xmName;

    private @NonNull DeliverSmsDeliverService smsDeliverService;
    private @NonNull DeliverEmailDeliverService emailDeliverService;
    private @NonNull DeliverWechatmpDeliverService wechatmpDeliverService;
    private @NonNull DeliverCustomLinkDeliverService customLinkDeliverService;
    private @NonNull DeliverEmbedDeliverService embedDeliverService;
    private @NonNull DeliverAlipayDeliverService alipayDeliverService;
    private @NonNull DeliverWechatDeliverService wechatDeliverService;
    private @NonNull DeliverAlipayTimingDeliverService alipayTimingDeliverService;
    private @NonNull DeliverWechatTimingDeliverService wechatTimingDeliverService;

    private @NonNull DeliverExtDeliverMappingMapper extDeliverMappingMapper;
    private @NonNull DeliverDeliverExtParamsMapper deliverExtParamsMapper;
    private @NonNull DeliverDeliverMapper deliverMapper;
    private @NonNull DeliverSurveyDeliverMapper surveyDeliver;

    private @NonNull OpenPlatformGrpcClient openPlatformGrpcClient;
    private @NonNull UserGrpcClient userGrpcClient;
    private @NonNull MailService mailService;


    @Override
    public void afterPropertiesSet() {
        event2StatusBiMap.put("start", DELIVER_STATUS_START.getValue());
        event2StatusBiMap.put("stop", DELIVER_STATUS_STOP.getValue());
        event2StatusBiMap.put("finish", DELIVER_STATUS_FINISH.getValue());
        event2StatusBiMap.put("delete", DELIVER_STATUS_DELETE.getValue());
        event2StatusBiMap.put("unreviewed", DELIVER_STATUS_UNREVIEWED.getValue());
        event2StatusBiMap.put("under_review", DELIVER_STATUS_UNDER_REVIEW.getValue());
        event2StatusBiMap.put("review_success", DELIVER_STATUS_REVIEW_SUCCESS.getValue());
        event2StatusBiMap.put("review_fail", DELIVER_STATUS_REVIEW_FAIL.getValue());
        event2StatusBiMap.put("done", DELIVER_STATUS_DONE.getValue());
        event2StatusBiMap.put("finish_stop", DELIVER_STATUS_FINISH_STOP.getValue());
        event2StatusBiMap.put("active", DELIVER_STATUS_ACTIVE.getValue());
        event2StatusBiMap.put("pause", DELIVER_STATUS_PAUSE.getValue());

        triggerDeliverStatusBiFunctionMapInit();
        checkDeliverStatusFunctionMapInit();
    }

    private void triggerDeliverStatusBiFunctionMapInit() {
        BiConsumer<DeliverDeliver, String> sms = (deliver, action) -> smsDeliverService.triggerDeliverStatus(deliver, action);
        BiConsumer<DeliverDeliver, String> email = (deliver, action) -> emailDeliverService.triggerDeliverStatus(deliver, action);
        BiConsumer<DeliverDeliver, String> customLink = (deliver, action) -> customLinkDeliverService.triggerDeliverStatus(deliver, action);
        BiConsumer<DeliverDeliver, String> wechatmp = (deliver, action) -> wechatmpDeliverService.triggerDeliverStatus(deliver, action);
        BiConsumer<DeliverDeliver, String> embed = (deliver, action) -> embedDeliverService.triggerDeliverStatus(deliver, action);

        triggerDeliverStatusBiFunctionMap.put(DELIVER_TTYPE_SMS, sms);
        triggerDeliverStatusBiFunctionMap.put(DELIVER_TTYPE_EMAIL, email);
        triggerDeliverStatusBiFunctionMap.put(DELIVER_TTYPE_CUSTOM_LINK, customLink);
        triggerDeliverStatusBiFunctionMap.put(DELIVER_TTYPE_WECHATMP, wechatmp);
        triggerDeliverStatusBiFunctionMap.put(DELIVER_TTYPE_EMBED, embed);
    }

    private void checkDeliverStatusFunctionMapInit() {
        Function<DeliverDeliver, Boolean> alipay = deliver -> alipayDeliverService.checkDeliverStatus(deliver);
        Function<DeliverDeliver, Boolean> wechat = deliver -> wechatDeliverService.checkDeliverStatus(deliver);
        Function<DeliverDeliver, Boolean> alipayTiming = deliver -> alipayTimingDeliverService.checkDeliverStatus(deliver);
        Function<DeliverDeliver, Boolean> wechatTiming = deliver -> wechatTimingDeliverService.checkDeliverStatus(deliver);

        checkDeliverStatusFunctionMap.put(DELIVER_TTYPE_ALIPAY, alipay);
        checkDeliverStatusFunctionMap.put(DELIVER_TTYPE_WECHAT, wechat);
        checkDeliverStatusFunctionMap.put(DELIVER_TTYPE_WECHAT_TIMING, wechatTiming);
        checkDeliverStatusFunctionMap.put(DELIVER_TTYPE_ALIPAY_TIMING, alipayTiming);
    }

    @Override
    public void triggerDeliverStatus(DeliverDeliver deliver, String action) {
        DeliverTtypeEnum deliveEnum = DeliverTtypeEnum.getEnumByLambda(deliver.getTtype());
        if (triggerDeliverStatusBiFunctionMap.containsKey(deliveEnum)) {
            triggerDeliverStatusBiFunctionMap.get(deliveEnum).accept(deliver, action);
        } else {
            log.error("triggerDeliverStatus do nothing");
        }
    }

    @Override
    public boolean checkDeliverStatus(DeliverDeliver deliver) {
        DeliverTtypeEnum deliveEnum = DeliverTtypeEnum.getEnumByLambda(deliver.getTtype());
        //固定返回true
        List<Integer> list = Lists.newArrayList(
                DELIVER_TTYPE_WECHATMP.getValue(),
                DELIVER_TTYPE_CUSTOM_LINK.getValue(),
                DELIVER_TTYPE_CUSTOM_API.getValue(),
                DELIVER_TTYPE_EMAIL.getValue(),
                DELIVER_TTYPE_DEFAULT_STORE.getValue(),
                DELIVER_TTYPE_REGION.getValue(),
                DELIVER_TTYPE_EMBED.getValue(),
                DELIVER_TTYPE_CUSTOM.getValue(),
                DELIVER_TTYPE_DEFAULT.getValue(),
                DELIVER_TTYPE_SMS.getValue()
        );
        if (list.stream().anyMatch(it -> it.equals(deliver.getTtype()))) {
            return true;
        } else if (checkDeliverStatusFunctionMap.containsKey(deliveEnum)) {
            return checkDeliverStatusFunctionMap.get(deliveEnum).apply(deliver);
        } else {
            return true;
        }
    }

    @Override
    public JSONObject startDeliver(DeliverDeliver deliver, Integer mpTtype) {
        String action;
        //如果是事件投放和二维码投放和嵌入投放，投放状态叫投放中
        List<Integer> list1 = ListUtil.of(
                DELIVER_TTYPE_REGION.getValue(),
                DELIVER_TTYPE_ALIPAY.getValue(),
                DELIVER_TTYPE_WECHAT.getValue(),
                DELIVER_TTYPE_EMBED.getValue());
        if (list1.stream().anyMatch(it -> it.equals(deliver.getTtype()))) {
            action = "start";
        } else if (Objects.equals(WECHATMPDELIVER_MP_TTYPE_CONDITION.getValue(), mpTtype)) {
            action = "active";
        } else {
            action = "done";
        }

        List<Integer> list2 = ListUtil.of(
                DELIVER_TTYPE_EMAIL.getValue(),
                DELIVER_TTYPE_SMS.getValue(),
                DELIVER_TTYPE_CUSTOM.getValue(),
                DELIVER_TTYPE_ALIPAY_TIMING.getValue(),
                DELIVER_TTYPE_WECHAT_TIMING.getValue(),
                DELIVER_TTYPE_CUSTOM_API.getValue(),
                DELIVER_TTYPE_WECHATMP.getValue());
        if (list2.stream().noneMatch(it -> it.equals(deliver.getTtype()))
                || Objects.equals(WECHATMPDELIVER_MP_TTYPE_CONDITION.getValue(), mpTtype)) {
            triggerDeliverStatusWrapper(deliver, action);
        } else {
            /*if (Stream.of(DELIVER_STATUS_STOP.getValue()).noneMatch(it -> it.equals(deliver.getStatus()))) {
                throw new BusinessException("当前投放状态不支持实施");
            }*/

            //base_url是处理定制接口投放customerapi的，这里不写，逻辑在DeliverTypeCustomAPI.py
            //base_url = f"{self.request.protocol}://{self.request.host}".rstrip("/") + "/"
            //await deliver_utils.trigger_delivercheck_status(deliver, action, base_url=base_url)
            triggerDeliverCheckStatusWrapper(deliver, action);
        }

        JSONObject ret = new JSONObject();
        ret.put("status", true);
        return ret;
    }

    @Override
    public JSONObject finishDeliver(DeliverDeliver deliver, Integer mpTtype) {

        if (deliver.getTtype().equals(DELIVER_TTYPE_WECHATMP.getValue())) {
            if (!mpTtype.equals(WECHATMPDELIVER_MP_TTYPE_CONDITION.getValue())) {
                throw new BusinessException("当前投放不能暂停");
            }

            deliverMapper.update(null,
                    new UpdateWrapper<DeliverDeliver>().lambda()
                            .eq(DeliverDeliver::getId, deliver.getId())
                            .set(DeliverDeliver::getStatus, DELIVER_STATUS_PAUSE.getValue()));

            JSONObject ret = new JSONObject();
            ret.put("status", true);
            return ret;
        }

        //只有事件投放可以结束  增加嵌入投放也可以结束投放
        if (Stream.of(DELIVER_TTYPE_ALIPAY.getValue(), DELIVER_TTYPE_WECHAT.getValue(), DELIVER_TTYPE_EMBED.getValue()).noneMatch(it -> it.equals(deliver.getTtype()))) {
            throw new BusinessException("当前投放不能结束");
        }

        deliverMapper.update(null,
                new UpdateWrapper<DeliverDeliver>().lambda()
                        .eq(DeliverDeliver::getId, deliver.getId())
                        .set(DeliverDeliver::getStatus, DELIVER_STATUS_FINISH.getValue()));

        if (deliver.getTtype().equals(DELIVER_TTYPE_EMBED.getValue())) {
            embedDeliverService.genEmbedJs(deliver.getId());
        }

        JSONObject ret = new JSONObject();
        ret.put("status", true);
        return ret;
    }

    @Async
    @Override
    public void deliverEmailCheckNotice(Integer channel, String deliverId, Project project) {
        log.info("Now to deliverEmailCheckNotice, channel = {}, deliverId = {}", channel, deliverId);
        // 批量发送审核通知邮件给审核人
        DeliverDeliver deliver = deliverMapper.selectById(deliverId);
        if (DeliverStatusEnum.DELIVER_STATUS_UNDER_REVIEW.getValue() != deliver.getStatus()) {
            log.error("The status error, status = {}", deliver.getStatus());
        }

        // 私有化写死
        String corpUrl = deliverDomain;
        String checkLink = deliverCheckUrlDict.get(channel);
        if (StringUtils.isEmpty(checkLink)) {
            log.error("The channel is error, channel = {}", channel);
            return;
        }
        checkLink = StrUtil.format(checkLink, corpUrl, project.getId(), deliverId);
        checkLink = StrUtil.format(deliverCheckUrlLabel, checkLink, checkLink);
        String currentDate = DateUtil.formatString(LocalDateTime.now(), "yyyy年MM月dd日");
        String content = StrUtil.format(deliverCheckMailTemplate, project.getTitle(), deliver.getName(), xmName, checkLink, xmName, currentDate);
        // 获取有审核权限的用户
        List<CheckUserInfo> checkers = userGrpcClient.getCheckers(deliver.getOrgId());
        OrgInfoDto organization = userGrpcClient.getOrganization(deliver.getOrgId());
        String smtpId = organization.getCheckSmtpId();
        log.info("Now to send email, smtpId = {}", smtpId);
        for (CheckUserInfo userInfo : checkers) {
            //邮件发送
            sendEmail(userInfo.getEmail(), "投放审核通知", content, smtpId);
        }
    }

    @Override
    public void deleteDeliver(DeliverDeliver deliver) {
        //await deliver_utils.trigger_deliver_status(deliver, "delete")
        triggerDeliverStatusWrapper(deliver, "delete");

        if (deliver.getTtype().equals(DELIVER_TTYPE_EMBED.getValue())) {
            embedDeliverService.genEmbedJs(deliver.getId());
        }

        if (deliver.getTtype().equals(DELIVER_TTYPE_CUSTOM_LINK.getValue())) {
            extDeliverMappingMapper.delete(new QueryWrapper<DeliverExtDeliverMapping>().
                    lambda().eq(DeliverExtDeliverMapping::getDeliverId, deliver.getId()));

            deliverExtParamsMapper.update(null,
                    new UpdateWrapper<DeliverDeliverExtParams>().lambda()
                            .eq(DeliverDeliverExtParams::getDeliverId, deliver.getId())
                            .set(DeliverDeliverExtParams::getStatus, DELIVEREXTPARAMS_STATUS_DELETE.getValue()));
        }

        int delete_count = openPlatformGrpcClient.deleteOpenCustomDeliverByDeliverId(SecurityContextHolder.getOrgId(), Stream.of(deliver.getId()).collect(Collectors.toList()));
        log.info("删除订制化投放配置条数:{}", delete_count);

        // 私有化不扣费
        //sms_deliver = await sms_utils.origin_get_smsdeliver_list(self.session, {"deliver_id": ObjectId(deliver.id)})
        //if sms_deliver:
        //await delete_pre_charge(self.session, orgID=org_id, sms_deliver_id=sms_deliver[0].oid, deliver=deliver)
    }

    private void triggerDeliverStatusWrapper(DeliverDeliver deliver, String action) {
        changeDeliverStatus(deliver, action);

        if ("start".equals(action) || "done".equals(action)) {
            boolean status = checkDeliverStatus(deliver);
            if (status) {
                triggerDeliverStatus(deliver, action);
            } else {
                throw new BusinessException("当前设置不完善，无法投放!");
            }
        }
    }

    //deliver.trigger(action)
    private void changeDeliverStatus(DeliverDeliver deliver, String action) {
        //await deliver.trigger(action) 里面有处理
        //class Deliver(Base, ModelBase, FysomGlobalMixin):
        //    __tablename__ = 'deliver_deliver'
        //    GSM = FysomGlobal(
        //        events=deliver_events.events,
        //        initial=deliver_events.initial,
        //        callbacks=deliver_events.callbacks,
        //        state_field='state'
        //    )
        //deliver_callbacks on 更新状态
        //deliver_callbacks 里面几个on函数

        //python只处理6个event，deliver_callbacks on开头的函数，
        //判断状态来源
        String old_status = event2StatusBiMap.inverse().get(deliver.getStatus());
        log.info("event: {}, src: {}, dst: {}", "on" + action, old_status, action);
        if (event2StatusBiMap.containsKey(action) && eventsMap.containsKey(action) && eventsMap.get(action).getSrc().contains(old_status)) {
            log.info("on{} triggered. new status is {}", action, action);
            deliverMapper.update(null,
                    new UpdateWrapper<DeliverDeliver>().lambda()
                            .eq(DeliverDeliver::getId, deliver.getId())
                            .eq(DeliverDeliver::getStatus, deliver.getStatus())
                            .set(DeliverDeliver::getStatus, event2StatusBiMap.get(action)));
        } else {
            log.error("deliver status is not" + deliver.getStatus());
            throw new BusinessException("deliver status is not" + deliver.getStatus());
        }
    }


    private void triggerDeliverCheckStatusWrapper(DeliverDeliver deliver, String action) {
        if ("done".equals(action) || "start".equals(action) || "review_success".equals(action)) {
            boolean status = checkDeliverStatus(deliver);
            if (status) {
                triggerDeliverStatus(deliver, action);
            } else {
                throw new BusinessException("当前设置不完善，无法投放!");
            }
        }
    }

    private JSONObject sendEmail(String emailTo, String subject, String content, String smtpId) {
        int status = EMAILRECORD_STATUS_SUCCESS.getValue();
        try {
            if (StrUtil.isNotEmpty(smtpId)) {
                log.info("Now to snedEmail, emailTo = {}, subject = {}, content = {}, smtpId = {}", emailTo, subject, content, smtpId);
                mailService.sendEmail(Collections.singletonList(emailTo), null, subject, content, smtpId, null);
            } else {
                //正常发送
                log.error("Now to snedEmail, no smtpId = {}", smtpId);
            }
        } catch (Exception e) {
            //发送异常时添加记录发送失败
            log.error("Error to send mail, e : ", e);
            status = EMAILRECORD_STATUS_FAILED.getValue();
        }
        JSONObject res = new JSONObject();
        res.put("status", status);
        return res;
    }

    @Data
    @AllArgsConstructor
    private static class EventObj {
        private List<String> src;
        private String dst;
    }
}




